/* * WBI Indicator Explorer * * Copyright 2015 Sebastian Nogara <snogaraleal@gmail.com> * * This file is part of WBI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package rpc.client.data; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONBoolean; import com.google.gwt.json.client.JSONNull; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONString; import com.google.gwt.json.client.JSONValue; import rpc.shared.data.Serializable; import rpc.shared.data.Serializer; import rpc.shared.data.SerializerException; import rpc.shared.data.Type; import rpc.shared.data.Utils; import rpc.shared.data.factory.NoSuitableSerializableFactory; import rpc.shared.data.factory.SerializableFactoryProvider; /** * Client-side JSON {@link Serializer}. */ @SuppressWarnings({"rawtypes", "unchecked"}) public class JSONSerializer implements Serializer { private SerializableFactoryProvider provider; /** * Initialize {@code JSONSerializer}. */ public JSONSerializer() {} /** * Initialize {@code JSONSerializer}. * * @param provider Provider of {@code Serializable} object instances. */ public JSONSerializer(SerializableFactoryProvider provider) { this.provider = provider; } /** * Produce a {@code JSONValue} from the specified {@code Object}. * * @param object {@code Object}. * @return {@code JSONValue}. */ private JSONValue toJSONValue(Object object) { // Null if (object == null) { return JSONNull.getInstance(); } // Boolean Boolean asBoolean = Utils.isBoolean(object); if (asBoolean != null) { return JSONBoolean.getInstance(asBoolean); } // Integer Integer asInteger = Utils.isInteger(object); if (asInteger != null) { return new JSONNumber(asInteger); } // Long Long asLong = Utils.isLong(object); if (asLong != null) { return new JSONNumber(asLong); } // Float Float asFloat = Utils.isFloat(object); if (asFloat != null) { return new JSONNumber(asFloat); } // Double Double asDouble = Utils.isDouble(object); if (asDouble != null) { return new JSONNumber(asDouble); } // String String asString = Utils.isString(object); if (asString != null) { return new JSONString(asString); } // Enum Enum asEnum = Utils.isEnum(object); if (asEnum != null) { return new JSONString(asEnum.toString()); } // Serializable Serializable asSerializable = Utils.isSerializable(object); if (asSerializable != null) { JSONObject jsonObject = new JSONObject(); for (String field : asSerializable.fields().keySet()) { Object value = asSerializable.get(field); jsonObject.put(field, toJSONValue(value)); } return jsonObject; } // List List<Object> asSerializableList = Utils.isSerializableList(object); if (asSerializableList != null) { JSONArray jsonArray = new JSONArray(); for (int i = 0; i < asSerializableList.size(); i++) { Object item = asSerializableList.get(i); jsonArray.set(i, toJSONValue(item)); } return jsonArray; } // Map Map<Object, Object> asSerializableMap = Utils.isSerializableMap(object); if (asSerializableMap != null) { JSONObject jsonObject = new JSONObject(); for (Map.Entry<Object, Object> entry : asSerializableMap.entrySet()) { Object keyObject = entry.getKey(); Object valueObject = entry.getValue(); String key = null; String keyAsString = Utils.isString(keyObject); if (keyAsString != null) { key = keyAsString; } Enum keyAsEnum = Utils.isEnum(keyObject); if (keyAsEnum != null) { key = keyAsEnum.toString(); } jsonObject.put(key, toJSONValue(valueObject)); } return jsonObject; } return null; } @Override public String serialize(Object object) throws SerializerException { if (!Utils.isSerializationCapable(object)) { throw new SerializerException( SerializerException.Error.NOT_SERIALIZABLE); } JSONValue jsonValue = toJSONValue(object); if (jsonValue == null) { throw new SerializerException( SerializerException.Error.NOT_SERIALIZABLE); } return jsonValue.toString(); } /** * Produce a new {@code Object} instance from the specified * {@code JSONValue}. * * @param jsonValue {@code JSONValue}. * @param expected Expected object {@link Type}. * @return New {@code Object}. * @throws NoSuitableSerializableFactory */ private Object fromJSONValue(JSONValue jsonValue, Type expected) throws NoSuitableSerializableFactory { if (jsonValue == null) { return null; } // Null JSONNull asNull = jsonValue.isNull(); if (asNull != null) { return null; } // Boolean JSONBoolean asBoolean = jsonValue.isBoolean(); if (asBoolean != null) { return asBoolean.booleanValue(); } // Integer // Long // Float // Double JSONNumber asNumber = jsonValue.isNumber(); if (asNumber != null) { double value = asNumber.doubleValue(); if (expected.isInteger()) { return (int) value; } if (expected.isLong()) { return (long) value; } if (expected.isFloat()) { return (float) value; } if (expected.isDouble()) { return value; } } // String // Enum JSONString asString = jsonValue.isString(); if (asString != null) { if (expected.isEnum()) { String value = asString.stringValue(); return Enum.valueOf( (Class) expected.getTypeClass(), value); } else { return asString.stringValue(); } } // Map // Serializable JSONObject asObject = jsonValue.isObject(); if (asObject != null) { if (expected.isMap()) { Map<Object, Object> map = new HashMap<Object, Object>(); Type keyType = expected.getParameterized(0); Type valueType = expected.getParameterized(1); if (!(keyType.isString() || keyType.isEnum())) { return null; } for (String key : asObject.keySet()) { JSONValue value = asObject.get(key); if (keyType.isString()) { map.put(key, fromJSONValue(value, valueType)); } if (keyType.isEnum()) { map.put( Enum.valueOf((Class) keyType.getTypeClass(), key), fromJSONValue(value, valueType)); } } return map; } else { if (provider == null) { throw new NoSuitableSerializableFactory(); } Serializable object = provider.make(expected); for (Map.Entry<String, Type> entry : object.fields().entrySet()) { String field = entry.getKey(); Type fieldType = entry.getValue(); JSONValue value = asObject.get(field); object.set(field, fromJSONValue(value, fieldType)); } return object; } } // List JSONArray asArray = jsonValue.isArray(); if (asArray != null) { int size = asArray.size(); List<Object> list = new ArrayList<Object>(); Type itemType = expected.getParameterized(0); for (int i = 0; i < size; i++) { JSONValue value = asArray.get(i); list.add(fromJSONValue(value, itemType)); } return list; } return null; } @Override public Object deserialize(String payload, Type expected) throws SerializerException { JSONValue jsonValue = JSONParser.parseStrict(payload); try { Object object = fromJSONValue(jsonValue, expected); if (object == null) { throw new SerializerException( SerializerException.Error.NOT_DESERIALIZABLE); } return object; } catch (NoSuitableSerializableFactory exception) { throw new SerializerException( SerializerException.Error.NOT_DESERIALIZABLE, exception); } } }